#coding=utf8
#-------------------------------------------------------------------------------
# Name:        ??1
# Purpose:
#
# Author:      weigun
#
# Created:     11-08-2011
# Copyright:   (c) weigun 2011
# Licence:     <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import pysvn
#import urllib
import os
import datetime
import re
import Log
class SvnClient():
    def __init__(self,url,username,password,debug = False):
        self.set_root(url)
        self.username = username or 'username'
        self.password = password or 'password'
        self.debug = debug
        self.tmp_folder = os.getenv('tmp') #--得到用户临时目录，用于diff时，存放临时文件
        self.type_map = {
            'add' : pysvn.diff_summarize_kind.added,
            'modified' : pysvn.diff_summarize_kind.modified,}
        self.client = pysvn.Client()
        self.client.callback_get_login = self._get_login
        self.client.callback_ssl_server_trust_prompt = self._ssl_server_trust_prompt

    def set_logger(self,logger):
        '''设置logger'''
        if isinstance(logger,Log.Log):
            self.logger = logger
        else:
            raise TypeError,'parameter is not a logger'
        return logger

    #--some call back
    def _get_login(self,realm, username, may_save ):
        if self.debug:
            print "_get_login callbck info:"
            print "realm:%s,username:%s,may_save:%s"%(realm,username,may_save)
        return True, self.username,self.password, False

    def _ssl_server_trust_prompt(self,trust_dict ):
        if self.debug:
            print "_ssl_server_trust_prompt callback info:"
            for k in trust_dict:
                print k,trust_dict[k]
        return True, True, True #--i can't do that

    def set_root(self,url):
        '''设置svn根目录，总是返回true'''
        self.svn_root = url
        return True

    def get_root(self):
        '''返回svn根目录'''
        return self.svn_root

    def catfile(self,path):
        '''将path和svn的根目录连接起来，返回完整的访问路径'''
        return '/'.join((self.get_root(),path))

    #--得到最新版本号，number
    def get_last_revision(self,path):
        '''得到服务器最新版本号的对象，return Revision obj '''
        return pysvn.Revision( pysvn.opt_revision_kind.head )

    def get_newest_rev_num(self,path = None):
        '''得到服务器最新版本号'''
        path = path or self.get_root()
        return self.get_info(path).next().rev.number

    #得到前一版本号，number
    def get_pre_revision(self,path):
        '''得到前一版本号的对象，相对于本地版本而言，return Revision obj，如果找不到该版本，返回None '''
        #--i dont think it is useful
        #--flag参数表示，是否使用pysvn.opt_revision_kind.previous，false表示不使用,而是使用本地版本号-1的Revision对象
        #if flag:
            #return pysvn.Revision( pysvn.opt_revision_kind.previous)

        return pysvn.Revision( pysvn.opt_revision_kind.number,self.get_local_revision(path) - 1)

    def get_revision_of(self,num_rev):
        '''得到版本号为num_rev的Revision obj，return Revision obj'''
        return pysvn.Revision( pysvn.opt_revision_kind.number,num_rev)

    def get_next_revision(self,path):
        '''得到后一版本号的对象，相对于本地版本而言,return Revision obj'''
        return pysvn.Revision( pysvn.opt_revision_kind.number,self.get_local_revision(path) + 1)




    def is_type_of(self,strtype,obj):
        '''obj是否为strtype类型，返回true or false'''
        return self.type_map[strtype] == obj

    #update
    def update(self,local_path = None):
        '''更新工作副本'''
        self.client.update(local_path)

    #得到更改的文件列表(包括新增加的文件)，返回列表
    def get_changed_files_obj(self,path,old_revision,new_revision = None): #--some problem must be solve
        '''得到不同版本间，更改的文件列表(包括新增加的文件)，返回变更对象列表.old_revision和new_revision可以是版本号或者Revision对象 '''
        if isinstance(old_revision, int):
            old_revision = pysvn.Revision( pysvn.opt_revision_kind.number,old_revision)
        if isinstance(new_revision, int):
            new_revision = pysvn.Revision( pysvn.opt_revision_kind.number,new_revision)
        else:
            new_revision = pysvn.Revision( pysvn.opt_revision_kind.head )
        if self.debug:
            print 'debug in get_changed_files_obj:old_revision is %s,new_revision is %s,path is %s'%(old_revision,new_revision,path)
        return self.client.diff_summarize(path,old_revision,path,new_revision)


    def changed_files_between(self,path,old_revision,new_revision = None): #--some problem must be solve
        '''返回一个已更改文件的生成器,不包括目录'''
        return (changed['path'] for changed in self.get_changed_files_obj(path,old_revision,new_revision) if changed['node_kind'] == pysvn.node_kind.file)
        #--return [changed['path'] for changed in self.get_changed_files_obj(path,old_revision,new_revision) if changed['node_kind'] == pysvn.node_kind.file]

    def changed_files(self,path,old_revision,new_revision = None):
        '''返回一个已更改文件的生成器,不包括目录'''
        #逐个逐个版本做对比，然后将发生改变的文件用字典保存
        files_mapper = {} #---内部记录的一个mapper
        new_revision = new_revision or self.get_newest_rev_num()
        new_rev_obj = None
        if self.debug:
            print 'debug in changed_files:'
        while old_revision != new_revision: #--直到相等，遍历完为止
            tmp_new_revision = old_revision + 1
            if self.debug:
                print '\told_revision is %s,new_revision is %s'%(old_revision,tmp_new_revision)
            old_rev_obj = new_rev_obj or self.get_revision_of(old_revision)
            new_rev_obj = self.get_revision_of(tmp_new_revision)
            #--与后版本diff_summarize
            for changed in self.get_changed_files_obj(path,old_rev_obj,new_rev_obj):
                if changed['node_kind'] == pysvn.node_kind.file:
                    if self.debug:
                        print 'found changed file:%s'%(changed['path'])
                    files_mapper.setdefault(changed['path'],1) #--insert into files_mapper
            old_revision = tmp_new_revision
        return (key for key in files_mapper)




    #get本地版本号，number
    def get_working_revision(self,path):
        '''返回工作目录版本号,return Revision obj'''
        return pysvn.Revision( pysvn.opt_revision_kind.working )


    def commit_changes(self,file_list,log):
        '''提交更改，参数为文件列表(使用全路径),日志'''
        self.client.checkin(file_list,log)

    def add(self,file_list,log):
        '''增加文件或目录'''
        self.client.add(file_list)
        self.commit_changes(file_list,log)

    def get_local_revision(self,local_path):
        '''返回local_path的版本号，值为number '''
        entry = svn.client.info(local_path)
        if self.debug:
            print u'SVN路径:',entry.url
            print u'最新版本:',entry.commit_revision.number
            print u'提交人员:',entry.commit_author
            print u'更新日期:', datetime.datetime.fromtimestamp(entry.commit_time)
        return entry.commit_revision.number

    def diff(self,path,old_rev,new_rev):
        '''比较2个版本之间的差异，返回差异内容'''
        try:
            return self.client.diff(self.tmp_folder,path,old_rev,path,new_rev)
        except pysvn.ClientError,e:
            print e
            self.logger.err_log(e)
            return None

    def get_info(self,path,rev = None):
        '''得到url的信息，返回一个消息的生成器'''
        '''
        The info_dict main contains:(info_dict is obj[1],see below code)
        URL - URL or path
        rev - pysvn.Revision or None
        kind - kind of path(dir or file)
        repos_root_URL - string
        repos_UUID - string
        last_changed_rev - pysvn.Revision or None
        last_changed_date - time or None
        last_changed_author - string or None
        '''
        if not rev:
            rev = pysvn.Revision( pysvn.opt_revision_kind.head )
        elif isinstance(rev, int):
            rev = pysvn.Revision( pysvn.opt_revision_kind.number,rev)
        try:
            return (obj[1] for obj in self.client.info2(path,rev))
        except pysvn.ClientError,e:
            print e
            self.logger.err_log(e)
            return None

    def get_info_by_num(self,path,num,rev=None):
        '''根据版本号获取info'''
        #--修改一下实现,直接调用get_info方法
        '''
        rev = rev or pysvn.Revision( pysvn.opt_revision_kind.number, num )
        try:
            return (obj[1] for obj in self.client.info2(path,rev))
        except pysvn.ClientError,e:
            print e
            return None
        '''
        return self.get_info(path,num)

    def get_log(self,path, start,end,log_limit = None):
        '''得到svn的log，返回一个列表，包含3个元素，每个元素为字典，key为版本号'''
        '''
        log returns a list of log entries; each log entry is a dictionary. The dictionary contains:

        author - string - the name of the author who committed the revision
        date - float time - the date of the commit
        message - string - the text of the log message for the commit
        revision - pysvn.Revision - the revision of the commit
        changed_paths - list of dictionaries. Each dictionary contains:
        path - string - the path in the repository
        action - string
        copyfrom_path - string - if copied, the original path, else None
        copyfrom_revision - pysvn.Revision - if copied, the revision of the original, else None
    '''
        if isinstance(start, int):
            start = pysvn.Revision( pysvn.opt_revision_kind.number,start)
        else:
            start = pysvn.Revision( opt_revision_kind.head )
        if isinstance(end, int):
            end = pysvn.Revision( pysvn.opt_revision_kind.number,end)
        else:
            end = pysvn.Revision( pysvn.opt_revision_kind.number,0)
        if self.debug:
            print 'debug in get_log:'
            print '\tstart revision is %s,end revision is %s,log_limit is %s'%(start,end,log_limit)
        try:
            return self.client.log(path,start,end,limit = log_limit or 0)
        except:
            return None

    def get_yesterday_all_revision(self,path):
        '''得到昨天所有的提交版本，返回一个revision生成器,否则返回None'''
        def rev_iter(number):
            while True:
                if number < 1:
                    break
                yield number
                number = number - 1
        rev_obj = self.get_info(path)#.next()
        if not rev_obj:
            print 'Error:get_info(%s) return None'%(path)
            self.logger.err_log('Error:get_info(%s) return None'%(path))
            return
        rev_obj = rev_obj.next()
        #--从最新版本开始遍历，寻找符合条件的
        rev_number =  rev_obj.rev.number #--版本号
        found = False
        #--
        debug = False
        #--print rev_number
        for cur_rev_number in rev_iter(rev_number):#--reversed(range(1,rev_number + 1)):
            if self.debug:
                print "debug in get_yesterday_all_revision:"
                print '\tcur_rev_number is %s'%(cur_rev_number)
            rev_obj = self.get_info(path,cur_rev_number).next()
            commit_data = datetime.datetime.fromtimestamp(rev_obj.last_changed_date)#--最后提交时间,转换为可读形式
            now_day = datetime.datetime.now().replace(hour = 0,minute = 0,second = 0,microsecond = 0)#--获得当前日期,无视时分秒
            commit_day = commit_data.replace(hour = 0,minute = 0,second = 0,microsecond = 0)#--获得提交日期,无视时分秒
            if debug:
                commit_day = now_day-datetime.timedelta(1)
            if self.debug:
                print '\tnow_day is %s,commit_day is %s'%(now_day,commit_day)
            if now_day - commit_day == datetime.timedelta(1):#--如果提交日期与现在日期相差1天,就从该版本开始，即无视12点后的提交
                found = True
                yield self.get_revision_of(cur_rev_number)
            elif not found:
                if now_day == commit_day:
                    continue
                if now_day - commit_day == datetime.timedelta(2):
                    yield self.get_revision_of(cur_rev_number)
                else:
                    break
            else:
                break

    def get_yesterday_begin_and_end_revision(self,path):
        '''得到昨天所有版本中，开始和结束的版本,返回字典{begin=rev,end=rev}'''
        rev_dict = {}
        obj_list = [rev_obj for rev_obj in self.get_yesterday_all_revision(path)]
        if self.debug:
            print 'debug in get_yesterday_begin_and_end_revision:'
            print '\tobj_list are',obj_list
        if obj_list:
            rev_dict['begin'] = obj_list[-1].number #--起始版本
            rev_dict['end'] = obj_list[0].number #--最后版本
        return rev_dict




if __name__=='__main__':
    #client = SvnClient()
    #client = pysvn.Client()
    #target = r'https://4399rzm01-PC/svn/PMP/'
    log = Log.Log()
    #svn = SvnClient(r'https://why-pc/svn/test/branches/a1.txt','why323','123456')
    #target = r'https://2011-20120218UX/svn/test/trunk'
    target = r'https://why-pc/svn/test/branches/a1.txt'
    #--https://4399rzm01-PC/svn/PMP/
    #target = r'https://2011-20120218UX/svn/test/trunk'
    target = r'https://4399rzm01-PC/svn/PMP/'
    svn = SvnClient(target,'weigun','123456')
    username = 'why323'
    path = os.getcwd() +'\cfg.cfg'
    path = r"F:\testsvn\a1.txt"
    path = r"D:\test-area\getItem\PMP\test.txt"
    #path = r'D:\tmpSvn'
    #path = r'D:\tmpSvn'

    diff13 = svn.diff(path,svn.get_revision_of(7),svn.get_revision_of(8))
    #iff23 = svn.diff(path,svn.get_revision_of(third_rev),svn.get_revision_of(second_rev))
    print diff13


